/*
Title: Bus Activity Monitor Compatible Uart Module
Author: Rajas Mokashi
Created for: ECE510: System Verilog Final Project
Description: This is a Uart module made for communication with the Bus Activity Monitor, 
             but is generic enough for use with any other project.
             It runs on 8.064 Mhz frequency
             It Supports programmable Baud Rate(Supporting most Standard baud rates)
             Can be programmed to use even/odd or none parity bit. 
             Suports 7Bit of data with parity/8bit of data without parity.
             Also has one/two stop bit support
*/

module UARTModule(BAMToUARTInterface FromBAM,
                  input Clock,
                  output logic Recieve,Transmit,Error);
  timeunit 1ns;
  timeprecision 1ns;
  
  
  import UARTPackage::*;                              // Importing Package
  
  logic [7:0] DataToSend, RecievedData, ControlWord=8'h84;
  logic TransmitFlag, RecieveFlag, ParityBit=0,MakeTransmitFlagHigh=0,MakeAcknowledgeHigh=0;
  logic RecieveParityBit;
  logic BaudClk;
  int RecieveDetectCount, BaudCount, SetBaudCount = Baud9600Count;
  TransmitStates TransmissionState=TIdle;
  RecieveStates ReceptionState=RIdle;
  int ParityCalc=0;
  
  initial
  begin
    Transmit = 1;
    TransmitFlag = 0;
    RecieveFlag = 0;
    BaudClk = 0;
    BaudCount = 0;
    FromBAM.TransmitAcknowledge = 0;
    RecieveParityBit = 1'bz;
  end
  
  always_comb                                                     // BAM to UART transmit transaction
    begin
      if(!FromBAM.TransmitAcknowledge)                            // If Transmit Acknowledge not high, reset MakeAcknowledgeHigh flag
        MakeAcknowledgeHigh = 0; 
      if(FromBAM.TransmitDataSent && !TransmitFlag && !FromBAM.TransmitAcknowledge)     // If not Transmitting data and new data available
        begin
          if(!FromBAM.ControlOrData)                              // If data on lines is data to be transmitted
            begin
              DataToSend <= FromBAM.TransmitData;                 // Latch data to be sent into DataToSend register
              if(ControlWord[6])                                  // If Parity used calculate parity
                begin
                  if(ControlWord[5])                              // Check for even or odd parity and calculate accordingly
                    ParityBit = 1'b0;
                  else
                    ParityBit = 1'b1;
                  for(ParityCalc=0;ParityCalc<7;ParityCalc++)
                    ParityBit = ParityBit^DataToSend[ParityCalc]; // Calculate and save parity for transmission phase
                end
                MakeTransmitFlagHigh = 1'b1;                      // Indicate that transmission should start
            end
          else                                                    // If data on lines is Control Word
            begin
              ControlWord = FromBAM.TransmitData;                 // Store control word
              case(ControlWord[3:0])                              // Change BaudCount based on new control word
                0: begin
                     SetBaudCount = Baud300Count;
                   end
                1: begin
                     SetBaudCount = Baud600Count;
                   end
                2: begin
                     SetBaudCount = Baud1200Count;
                   end
                3: begin
                     SetBaudCount = Baud2400Count;
                   end
                4: begin
                     SetBaudCount = Baud9600Count;
                   end
                5: begin
                     SetBaudCount = Baud14400Count;
                   end
                6: begin
                     SetBaudCount = Baud19200Count;
                   end
                7: begin
                     SetBaudCount = Baud28800Count;
                   end
                8: begin
                     SetBaudCount = Baud38400Count;
                   end
                9: begin
                     SetBaudCount = Baud56000Count;
                    end
                10: begin
                     SetBaudCount = Baud57600Count;
                    end
                11: begin
                     SetBaudCount = Baud115200Count;
                    end
                default: begin
                     SetBaudCount = Baud9600Count;
                     $display("Set Default Baud Count");
                    end
              endcase
              MakeAcknowledgeHigh = 1'b1;                         // Indicate control word latched, send acknowledge signal
            end
        end
      else
        begin
          if(!TransmitFlag)
            MakeTransmitFlagHigh = 1'b0;                          // Reset Transmit flag indicator if not transmitting
        end

    end
  
  always_ff @(posedge BaudClk)                                    // Byte Transmit Logic
    begin
      if(MakeTransmitFlagHigh)                                    // Make transmit flag high if data to be transmitted
        TransmitFlag <=1;
      if(TransmitFlag == 1)                                       // If Tranmitting
        begin
          TransmissionState <= TransmissionState.next();
          unique case(TransmissionState)                          // One hot FSM for Transmission of data
            TIdle:      begin
                        end
            TStart:     begin
                        Transmit <= 0;                            // Transmit Start bit
                        end
            TBit1:      begin
                        Transmit <= DataToSend[0];                // Transmit data bit 1
                        end
            TBit2:      begin
                        Transmit <= DataToSend[1];                // Transmit data bit 2
                        end
            TBit3:      begin
                        Transmit <= DataToSend[2];                // Transmit data bit 3
                        end
            TBit4:      begin
                        Transmit <= DataToSend[3];                // Transmit data bit 4
                        end
            TBit5:      begin
                        Transmit <= DataToSend[4];                // Transmit data bit 5
                        end
            TBit6:      begin
                        Transmit <= DataToSend[5];                // Transmit data bit 6
                        end
            TBit7:      begin
                        Transmit <= DataToSend[6];                // Transmit data bit 7
                        end
            TBit8:      begin
                        if(ControlWord[6]==0)
                          Transmit <= DataToSend[7];              // Send data bit 8(MSB)
                        else
                          Transmit <= ParityBit;                  // Or if using parity, transmit calculated parity
                        end
            TStopBit1:  begin
                        Transmit <= 1; 
                        if(ControlWord[7]==0)                     // If only one stop bit used, stop transmission
                          begin
                          TransmitFlag <= 0;
                          FromBAM.TransmitAcknowledge <= 1;
                          end
                        end
            TStopBit2:  begin
                        Transmit <= 1;                            // Stop transmission after sending 2 stop bits
                        TransmitFlag <= 0;
                        FromBAM.TransmitAcknowledge <= 1;
                        end
            default:    begin
                        TransmissionState <= TransmissionState.first();   // If some error occured, go to idle state
                        TransmitFlag <= 0;                                // And stop current transmission
                        end
          endcase
        end
      else
        TransmissionState <= TransmissionState.first();           // If not transmitting go to Idle state
    end
  
    always_ff @(posedge BaudClk)                                    // Byte Recieve Logic
    begin
      if(RecieveFlag)
        begin
          ReceptionState <= ReceptionState.next();
          unique case(ReceptionState)
            RIdle:      begin
                        end
            RBit1:      begin
                          RecievedData[0] <= Recieve;             // Recieve data bit 1
                        end
            RBit2:      begin
                          RecievedData[1] <= Recieve;             // Recieve data bit 2
                        end
            RBit3:      begin
                          RecievedData[2] <= Recieve;             // Recieve data bit 3
                        end
            RBit4:      begin
                          RecievedData[3] <= Recieve;             // Recieve data bit 4
                        end
            RBit5:      begin
                          RecievedData[4] <= Recieve;             // Recieve data bit 5
                        end
            RBit6:      begin
                          RecievedData[5] <= Recieve;             // Recieve data bit 6
                        end
            RBit7:      begin
                          RecievedData[6] <= Recieve;             // Recieve data bit 7
                          if(ControlWord[5])                      // Set initial parity before calculation
                            RecieveParityBit <= 1'b0;
                          else
                            RecieveParityBit <= 1'b1;
                        end
            RBit8:      begin
                        RecievedData[7] <= Recieve;               // Recieve data bit 8
                        if(ControlWord[6])                        // If recieved bit is parity bit,
                          begin                                   // check if calculated and recieved parity match
                            for(int RecieveParityCalc=0;RecieveParityCalc<7;RecieveParityCalc++)
                              RecieveParityBit <= RecieveParityBit^RecievedData[RecieveParityCalc];         //Calculate and save parity to check recived byte
                            if(RecieveParityBit != RecievedData[7])
                              Error<=1;                           // If not matched, indicate error
                          end
                        end
            RStopBit1:  begin
                          FromBAM.RecieveData <= RecievedData;    // Recieve stop bit 1
                          FromBAM.RecieveDataSent <= 1'b1;
                          if(ControlWord[7]==0)
                            if(Recieve==1)                        // If stop bit 1 not recieved, indicate error
                              RecieveFlag <= 0;
                            else
                              Error<=1;
                        end
            RStopBit2:  begin
                          if(Recieve==1)
                            RecieveFlag <= 0;
                          else
                            Error<=1;                             // If stop bit 2 not recieved, indicate error
                        end
          endcase
        end  
      else
        ReceptionState <= ReceptionState.first();                 // Else reset state to Idle State
    end
  
  always @(negedge BaudClk)                                       // Reset error flag after half baud clock
  begin
    if(Error)
      Error <= 0;
  end
  
  always_ff @(posedge Clock)
    begin
      if(MakeAcknowledgeHigh)
        FromBAM.TransmitAcknowledge <= 1'b1;                      // Make acknowledge high if indicated
      if(!RecieveFlag && !Recieve)                                // Recieve bit detection logic
        begin
          RecieveDetectCount <= RecieveDetectCount+1;
          if(RecieveDetectCount>30)
            begin
              RecieveDetectCount <= 0;
              RecieveFlag <= 1;
            end
        end
      BaudCount <= BaudCount+1;                                   // Baud Clock Generation Logic
      if(BaudCount>=SetBaudCount)
        begin
          BaudCount <= 0;
          BaudClk = ~BaudClk;
        end
    end

  always_ff @(negedge Clock)                                      // Reset acknowledge
  begin
    FromBAM.TransmitAcknowledge <= 1'b0;
  end
    
  always_ff @(posedge FromBAM.RecieveAcknowledge)                 // If data sent to BAM was recieved,
  begin
    FromBAM.RecieveDataSent <= 1'b0;                              // Then reset RecieveDataSent indicator for next recieved byte
  end
  
endmodule